/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.overall;

import com.ctc.wstx.util.StringUtil;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.inspur.edp.caf.generator.BaseGenerator;
import com.inspur.edp.caf.generator.BaseGeneratorContext;
import com.inspur.edp.caf.generator.baseInfo.AnnotationInfo;
import com.inspur.edp.caf.generator.baseInfo.AnnotationType;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.cef.api.attr.PropertyGetAtrribute;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.entity.entity.IChildEntityData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IKey;
import com.inspur.edp.cef.gencommon.CefPropertyInfo;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.gencommon.ReflectionUtils;
import com.inspur.edp.cef.generator.exception.ErrorCodes;
import com.inspur.edp.cef.spi.jsonser.abstractcefchange.BefDateSerializer;
import com.inspur.edp.cef.spi.jsonser.abstractcefchange.BefDateTimeDeseiralizer;
import com.inspur.edp.cef.spi.jsonser.abstractcefchange.BefDateTimeSerializer;
import com.inspur.edp.udt.entity.IUdtData;
import io.iec.edp.caf.databaseobject.api.entity.DataType;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;

import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.springframework.util.StringUtils;

public final class GeneratorUtil {
//
//
//  public static Class getType(CompileContext compileContext, String className) {
//    if (compileContext == null) {
//      return CefConfigUtils.getType("", className);
//    } else {
//      ArrayList<String> jarList = new ArrayList<String>() {
//        {
//          add(compileContext.getApiJarFileName());
//          add(compileContext.getCoreJarFileName());
//          add(compileContext.getCompJarFileName());
//        }
//      };
//      return CefConfigUtils.getType(jarList, "", className);
//    }
//  }

  //ORIGINAL LINE: public static string remove(this string interfaceName)
  public static String remove(String interfaceName) {
    if (interfaceName == null || !interfaceName.startsWith("I")) {
      return interfaceName;
    }
    return interfaceName.substring(1, interfaceName.length());
  }


  //ORIGINAL LINE: public static string getName(this string configId)
  public static String getName(String configId) {
    int index = configId.lastIndexOf(".");
    return configId.substring(index + 1, index + 1 + configId.length() - index - 1);
  }

  public static String getConfigName(String configID) {
    int index = configID.lastIndexOf(".");
    return configID.substring(index + 1, index + 1 + configID.length() - index - 1);
  }

  public static java.lang.Class getPropertyType(java.lang.Class host, String propName) {
    Method[] pi = host.getMethods();
    for (Method m : pi) {
      if (m.getName().equals("get" + propName)) {
        return m.getReturnType();
      }
    }

    for (Class baseType : host.getInterfaces()) {
      if (baseType.equals(IEntityData.class) || baseType.equals(IChildEntityData.class)
          || baseType.equals(IKey.class)) {
        continue;
      }
      pi = host.getMethods();
      for (Method m : pi) {
        if (m.getName().equals("get" + propName)) {
          return m.getReturnType();
        }
      }
    }
    return null;
  }

  //ORIGINAL LINE: internal static bool IsGeneratedInterfaceType(this Type host, Type interfaceType)
  public static boolean isGeneratedInterfaceType(java.lang.Class host, java.lang.Class interfaceType) {

    for (Class item : host.getInterfaces()) {
      if (interfaceType == item) {
        return true;
      }
    }
    return false;
  }

  public static java.util.List<CefPropertyInfo> getProps(java.lang.Class dataInterfaceType) {
    DataValidator.checkForNullReference(dataInterfaceType, "dataInterfaceType");

    java.util.ArrayList<java.lang.Class> distinctTypes = new java.util.ArrayList<java.lang.Class>();
    java.util.ArrayList<CefPropertyInfo> rez = new java.util.ArrayList<CefPropertyInfo>();
    java.util.LinkedList<java.lang.Class> types = new java.util.LinkedList<java.lang.Class>();
    types.offer(dataInterfaceType);
    while (types != null && types.size() > 0) {
      Class t = types.poll();
      distinctTypes.add(t);
      for (Class baseOfT : t.getInterfaces()) {
        if (baseOfT.getPackage() == IEntityData.class.getPackage() || baseOfT.getPackage() == IUdtData.class.getPackage()) {
          continue;
        }
        types.offer(baseOfT);
      }
    }
    for (Object t : new ArrayList(new HashSet(distinctTypes))) {
      Class type = (Class) t;
      for (Method method : type.getMethods()) {
        PropertyGetAtrribute propertyGetAtrribute = (PropertyGetAtrribute) ReflectionUtils.getMethodAnnotation(method, PropertyGetAtrribute.class);
        if (propertyGetAtrribute == null)
          continue;
        rez.add(new CefPropertyInfo(propertyGetAtrribute.propName(), method));
      }
    }
    Collections.reverse(rez);
    return rez;
  }

  public static MethodInvocation getgetInnerDataStatemnet(AST ast) {
    MethodInvocation methodInvocation = ast.newMethodInvocation();
    methodInvocation.setName(ast.newSimpleName("getInnerData"));
    return methodInvocation;

  }

  //(TypeName)variableName  ,ex: (ModifyChangeDetail)change
  public static CastExpression getSimpleCastExpression(AST ast, String typeName, String variableName) {
    CastExpression castExpression = ast.newCastExpression();
    castExpression.setType(ast.newSimpleType(ast.newName(typeName)));
    castExpression.setExpression(ast.newSimpleName(variableName));
    return castExpression;
  }

  public static StringLiteral getStringLiteral(AST ast, String escapedValue) {
    StringLiteral stringLiteral = ast.newStringLiteral();
    stringLiteral.setLiteralValue(escapedValue);
    return stringLiteral;
  }


//	public static String toCamel(String value) =>StringUtils.ToCamelCase(value);

  /**
   * 将dbo数据类型转换为Cef.Api中GspDbDataType
   *
   * @param dataType
   * @return
   */
  public static GspDbDataType transDataType(DataType dataType) {
    GspDbDataType dbDataType = GspDbDataType.VarChar;
    switch (dataType) {
      case Char:
        dbDataType = GspDbDataType.Char;
        break;
      case Varchar:
        dbDataType = GspDbDataType.VarChar;
        break;
      case Blob:
        dbDataType = GspDbDataType.Blob;
        break;
      case DateTime:
        dbDataType = GspDbDataType.Date;
        break;
      case TimeStamp:
        dbDataType = GspDbDataType.DateTime;
        break;
      case Clob:
        dbDataType = GspDbDataType.Clob;
        break;
      case Int:
      case SmallInt:
        dbDataType = GspDbDataType.Int;
        break;
      case Decimal:
        dbDataType = GspDbDataType.Decimal;
        break;
      case NChar:
        dbDataType = GspDbDataType.NChar;
        break;
      case NVarchar:
        dbDataType = GspDbDataType.NVarChar;
        break;
      case NClob:
        dbDataType = GspDbDataType.NClob;
        break;
    }
    return dbDataType;
  }

  public static ParameterInfo buildParameterInfo(Parameter parameter) {
    ParameterInfo info = new ParameterInfo();
    info.setParamName(parameter.getName());
    TypeInfo typInfo;
    typInfo = buildTypeInfo(parameter.getParameterizedType());
    info.setParamType(typInfo);
    return info;
  }

  public static ParameterInfo createParameterInfo(MediateType type, String paraName) {
    return createParameterInfo(new TypeInfo(type.getFullName()), paraName);
  }

  public static ParameterInfo createParameterInfo(Class type, String paraName) {
    return createParameterInfo(new TypeInfo(type), paraName);
  }

  public static ParameterInfo createParameterInfo(TypeInfo type, String paraName) {
    ParameterInfo info = new ParameterInfo();
    info.setParamType(type);
    info.setParamName(paraName);
    return info;
  }

  private static TypeInfo buildTypeInfo(Type type) {
    TypeInfo typInfo;
    if (type instanceof java.lang.reflect.ParameterizedType) {
      java.lang.reflect.ParameterizedType ptype = (java.lang.reflect.ParameterizedType)type;
      typInfo = new TypeInfo((Class) ptype.getRawType());
      for (java.lang.reflect.Type ta : ptype.getActualTypeArguments()) {
        typInfo.getArguments().add(buildTypeInfo(ta));
      }
      typInfo.setIsParameterizedType(true);
    } else {
      typInfo = new TypeInfo(((Class) type).getTypeName());
      typInfo.setUseFullName(true);
    }
    return typInfo;
  }

  public static QualifiedName buildQualifiedName(AST ast, Class type) {
    Objects.requireNonNull(type, "type");
    String[] items = type.getTypeName().split("[.]");
    QualifiedName rez = ast.newQualifiedName(ast.newSimpleName(items[0]),
        ast.newSimpleName(items[1]));
    for (int j = 2; j < items.length; j++) {
      rez = ast.newQualifiedName(rez, ast.newSimpleName(items[j]));
    }
    return rez;
  }

  public static AnnotationInfo getBefDateDeserializerAnnoInfo(AST ast) {
    return AnnotationInfo.buildNormal(new TypeInfo(JsonDeserialize.class),
        Collections.singletonMap("using",
            GenUtils.buildTypeLiteral(ast, BefDateTimeDeseiralizer.class.getName())));
  }

  public static AnnotationInfo getBefDateSerializerAnnoInfo(AST ast) {
    return AnnotationInfo.buildNormal(new TypeInfo(JsonSerialize.class),
        Collections.singletonMap("using",
            GenUtils.buildTypeLiteral(ast, BefDateSerializer.class.getName())));
  }


  public static AnnotationInfo getBefDateTimeDeserializerAnnoInfo(AST ast) {
    return AnnotationInfo.buildNormal(new TypeInfo(JsonDeserialize.class),
        Collections.singletonMap("using",
            GenUtils.buildTypeLiteral(ast, BefDateTimeDeseiralizer.class.getName())));
  }

  public static AnnotationInfo getBefDateTimeSerializerAnnoInfo(AST ast) {
    return AnnotationInfo.buildNormal(new TypeInfo(JsonSerialize.class),
        Collections.singletonMap("using",
            GenUtils.buildTypeLiteral(ast, BefDateTimeSerializer.class.getName())));
  }

  public static ThrowStatement getThrowStatement( AST ast, String message, String paramName,boolean messageInLeft){
    InfixExpression infixExpression=ast.newInfixExpression();
    StringLiteral stringLiteral =ast.newStringLiteral();
    if (message!=null && !message.isEmpty() && paramName!=null && !paramName.isEmpty()){
      infixExpression.setOperator(InfixExpression.Operator.PLUS);
      stringLiteral.setLiteralValue(message);
      if(messageInLeft){
        infixExpression.setLeftOperand(stringLiteral);
        infixExpression.setRightOperand(ast.newSimpleName(paramName));
      }else {
        infixExpression.setLeftOperand(ast.newSimpleName(paramName));
        infixExpression.setRightOperand(stringLiteral);
      }
    }else{
      if (message!=null && !message.isEmpty()){
        return getThrowStatementWithMessage(ast,message);
      }else if(paramName!=null && !paramName.isEmpty()){
        return getThrowStatementWithParamName(ast,paramName);
      }
    }
    return getThrowStatement(ast,infixExpression);
  }

  public static ThrowStatement getThrowStatement(AST ast, String message,MethodInvocation methodInvocation,boolean messageInLeft){
    InfixExpression infixExpression=ast.newInfixExpression();
    StringLiteral stringLiteral =ast.newStringLiteral();
    if (message!=null && !message.isEmpty() && methodInvocation!=null){
      infixExpression.setOperator(InfixExpression.Operator.PLUS);
      stringLiteral.setLiteralValue(message);
      if(messageInLeft){
        infixExpression.setLeftOperand(stringLiteral);
        infixExpression.setRightOperand(methodInvocation);
      }else {
        infixExpression.setLeftOperand(methodInvocation);
        infixExpression.setRightOperand(stringLiteral);
      }
    }else{
      if (message!=null && !message.isEmpty()){
        return getThrowStatementWithMessage(ast,message);
      }
    }
    return getThrowStatement(ast,infixExpression);

  }

  public static ThrowStatement getThrowStatement(AST ast,Expression expression){
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    if (expression!=null && !StringUtils.isEmpty(expression)){
      classInstanceCreation.arguments().add(expression);
    }
    return setThrowStatement(classInstanceCreation,ast);
  }

  private static ThrowStatement setThrowStatement(ClassInstanceCreation classInstanceCreation,AST ast){
    ThrowStatement throwStatement = ast.newThrowStatement();
    classInstanceCreation.setType(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(CefException.class).getType(ast));
    classInstanceCreation.arguments().add(getStringLiteral(ast,ErrorCodes.Exception_Code));
    throwStatement.setExpression(classInstanceCreation);
    return throwStatement;
  }

  public static ThrowStatement getThrowStatementWithMessage(AST ast, String message){
     return getThrowStatement(ast,getStringLiteral(ast,message));
  }

  public static ThrowStatement getThrowStatementWithParamName(AST ast, String paramName){
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    classInstanceCreation.arguments().add(ast.newSimpleName(paramName));
    return setThrowStatement(classInstanceCreation,ast);
  }



}
